package org.pearl.boot.mybatis.demo.config;

/**
 * Created by TD on 2021/6/16
 */
/*
 *    Copyright 2015-2021 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Stream;

/**
 * MyBatis属性配置
 */
// @ConfigurationProperties。只要在bean上添加上这个注解，指定好配置文件的前缀，那么对应的配置文件数据就会自动填充到bean中
// @EnableConfigurationProperties({MybatisProperties.class})会使@ConfigurationProperties 注解的类生效
// 会读取mybatis前缀的配置加载到当前Bean中
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {

    // 配置文件前缀
    public static final String MYBATIS_PREFIX = "mybatis";

    private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();

    // MyBatis xml配置文件的位置
    private String configLocation;

    // MyBatis xml映射文件的位置
    private String[] mapperLocations;

    // 别名包的位置，使用分隔符（,; \t\n"）
    private String typeAliasesPackage;

    // 别名类的父类。指定之后，只会加载配置了此超类的文件，如果没有指定，MyBatis会将所有从 ypeAliasesPackage中搜索到的类作为类型别名处理
    private Class<?> typeAliasesSuperType;

    // 扫描类型处理器所在的包，使用分隔符（,; \t\n"）
    private String typeHandlersPackage;

    // 指示是否对 MyBatis xml 配置文件进行存在检查, 使用了自动装配，所以不需要xml配置了
    private boolean checkConfigLocation = false;

    // sql执行器，可配置项：SIMPLE、REUSE、BATCH，
    // SIMPLE是默认执行器，根据对应的sql直接执行，不会做一些额外的操作
    // REUSE是可重用执行器，重用对象是Statement（即该执行器会缓存同一个sql的Statement，省去Statement的重新创建，优化性能）（即会重用预处理语句）
    // BATCH执行器会重用预处理语句，并执行批量更新。
    private ExecutorType executorType;

    // 默认脚本语言驱动程序类。 (与mybatis-spring 2.0.2+一起使用时可用)
    private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;

    // MyBatis外部配置属性
    private Properties configurationProperties;

    // 配置对象，如果指定了configLocation，也不使用此属性
    @NestedConfigurationProperty // 表示这是一个嵌套类型
    private Configuration configuration;

    /**
     * @since 1.1.0
     */
    public String getConfigLocation() {
        return this.configLocation;
    }

    /**
     * @since 1.1.0
     */
    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    public String[] getMapperLocations() {
        return this.mapperLocations;
    }

    public void setMapperLocations(String[] mapperLocations) {
        this.mapperLocations = mapperLocations;
    }

    public String getTypeHandlersPackage() {
        return this.typeHandlersPackage;
    }

    public void setTypeHandlersPackage(String typeHandlersPackage) {
        this.typeHandlersPackage = typeHandlersPackage;
    }

    public String getTypeAliasesPackage() {
        return this.typeAliasesPackage;
    }

    public void setTypeAliasesPackage(String typeAliasesPackage) {
        this.typeAliasesPackage = typeAliasesPackage;
    }

    /**
     * @since 1.3.3
     */
    public Class<?> getTypeAliasesSuperType() {
        return typeAliasesSuperType;
    }

    /**
     * @since 1.3.3
     */
    public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
        this.typeAliasesSuperType = typeAliasesSuperType;
    }

    public boolean isCheckConfigLocation() {
        return this.checkConfigLocation;
    }

    public void setCheckConfigLocation(boolean checkConfigLocation) {
        this.checkConfigLocation = checkConfigLocation;
    }

    public ExecutorType getExecutorType() {
        return this.executorType;
    }

    public void setExecutorType(ExecutorType executorType) {
        this.executorType = executorType;
    }

    /**
     * @since 2.1.0
     */
    public Class<? extends LanguageDriver> getDefaultScriptingLanguageDriver() {
        return defaultScriptingLanguageDriver;
    }

    /**
     * @since 2.1.0
     */
    public void setDefaultScriptingLanguageDriver(Class<? extends LanguageDriver> defaultScriptingLanguageDriver) {
        this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver;
    }

    /**
     * @since 1.2.0
     */
    public Properties getConfigurationProperties() {
        return configurationProperties;
    }

    /**
     * @since 1.2.0
     */
    public void setConfigurationProperties(Properties configurationProperties) {
        this.configurationProperties = configurationProperties;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    public Resource[] resolveMapperLocations() {
        return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0]))
                .flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new);
    }

    private Resource[] getResources(String location) {
        try {
            return resourceResolver.getResources(location);
        } catch (IOException e) {
            return new Resource[0];
        }
    }

}